package org.xm.sk.service.data.impl;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import javax.annotation.PostConstruct;
import javax.persistence.EntityManager;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.domain.Example;
import org.springframework.data.domain.ExampleMatcher;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.domain.Sort.Direction;
import org.springframework.data.domain.Sort.Order;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.xm.sk.dao.TestRepository;
import org.xm.sk.service.data.BaseService;
import org.xm.sk.service.security.XmUser;
import org.xm.sk.vo.ob.Local;
import org.xm.sk.vo.ob.PageConf;
import org.xm.sk.vo.tb.QShop;
import org.xm.sk.vo.tb.QTest;
import org.xm.sk.vo.tb.Test;

import com.querydsl.core.types.dsl.Expressions;
import com.querydsl.core.types.dsl.MathExpressions;

import static com.querydsl.core.types.dsl.MathExpressions.radians;
import static com.querydsl.core.types.dsl.MathExpressions.acos;
import static com.querydsl.core.types.dsl.MathExpressions.sin;
import static com.querydsl.core.types.dsl.MathExpressions.cos;
import com.querydsl.core.types.dsl.NumberExpression;
import com.querydsl.core.types.dsl.NumberPath;
import com.querydsl.jpa.impl.JPAQueryFactory;

@Service
@Transactional
public class TestServiceImpl extends BaseServiceImpl<Test> {

    private static final String CACHE_KEY = "'test'";
    private static final String sk_CACHE_NAME = "tests";

    //实体管理者
    @Autowired
    private EntityManager entityManager;

    //JPA查询工厂
    private JPAQueryFactory queryFactory;
    
    @Autowired
    private TestRepository testRepository;
    
    @PostConstruct
    public void initFactory() {
        queryFactory = new JPAQueryFactory(entityManager);
        System.out.println("init JPAQueryFactory successfully");
    }

    @Cacheable(value = sk_CACHE_NAME, key = CACHE_KEY + "'_'+#p0")
    public Test findById(int id) {
        return this.testRepository.getOne(id);
    }

    @Cacheable(value = sk_CACHE_NAME, key = CACHE_KEY + "'_'+#p0")
    public Object findByName(String name) {
        QTest _Q_test = QTest.test;
        //return this.testRepository.findAll(qtest.name.contains(name));
        
        //return queryFactory.selectFrom(qtest).where(qtest.name.contains(name)).fetch();

        Local local = new Local();
        local.setLatitude(23.372223);
        local.setLongitude(116.718995);
        
        /*
                    sqrt(
                        (
                            (
                                (?1 - u.longitude) * PI() * 12656 * cos(
                                    ((?2 + u.latitude) / 2) * PI() / 180
                                ) / 180
                            ) * (
                                (?1 - u.longitude) * PI() * 12656 * cos(
                                    ((?2 + u.latitude) / 2) * PI() / 180
                                ) / 180
                            )
                        ) + (
                            (
                                (?2 - u.latitude) * PI() * 12656 / 180
                            ) * (
                                (?2 - u.latitude) * PI() * 12656 / 180
                            )
                        )
                    )
         */
        NumberPath<Double> lat = _Q_test.latitude;
        NumberPath<Double> lng = _Q_test.longitude;
        NumberPath<Double> distance = null;
        NumberExpression<Double> formula = 
                (acos(cos(radians(Expressions.constant(local.getLatitude())))
                .multiply(cos(radians(lat))
                .multiply(cos(radians(lng).subtract(radians(Expressions.constant(local.getLongitude())))
                .add(sin(radians(Expressions.constant(local.getLatitude())))
                .multiply(sin(radians(lat))))))))
                .multiply(Expressions.constant(6371)));
        NumberExpression<Double> formula1 = MathExpressions.exp(lat);
        return this.testRepository.findAll(formula1.as(distance).lt(2));
        //return queryFactory.selectFrom(_Q_test).where(lat.lt(23.367919)).fetch();
        
    }

    @Cacheable(value = sk_CACHE_NAME, key = CACHE_KEY + "'_all'")
    public List<Test> findAll() {
        return this.testRepository.findAll();
    }

    @Cacheable(value = sk_CACHE_NAME, key = CACHE_KEY + "'_'+#p0")
    public List<Test> findAll(Test test) {
        ExampleMatcher matcher = ExampleMatcher.matchingAny();
        Example<Test> ex = Example.of(test, matcher);
        return this.testRepository.findAll(ex);
    }

    @Cacheable(value = sk_CACHE_NAME, key = CACHE_KEY + "'_'+#p0")
    public Object findAllByPage(PageConf pageConf) {
        List<Order> orders = new ArrayList<Order>();
        if (null != pageConf.getSortNames()) {
            for (int i = 0; i < pageConf.getSortNames().size(); i++) {
                orders.add(new Order(pageConf.getSortOrders().get(i),
                        pageConf.getSortNames().get(i)));
            }
        } else {
            orders.add(new Order(Direction.ASC, "id"));
        }
        Sort sort = Sort.by(orders);
        if (null != pageConf.getPage() && null != pageConf.getSize()) {
            Pageable pageable = PageRequest.of(pageConf.getPage(),
                    pageConf.getSize(), sort);
            return this.testRepository.findAll(pageable);
        } else {
            return this.testRepository.findAll(sort);
        }
    }
    
    public Page<Test> findAllByPage(PageConf pageConf, Object object) {
        return null;
    }

    @CacheEvict(value = sk_CACHE_NAME, allEntries=true)
    //@CachePut(value = sk_CACHE_NAME, key = CACHE_KEY + "'_'+#p0.id")
    public Test update(Test test) {
        return this.testRepository.save(test);
    }

    @CacheEvict(value = sk_CACHE_NAME, allEntries=true)
    public void delete(int id) {
        this.testRepository.deleteById(id);
    }
    
    @CacheEvict(value = sk_CACHE_NAME, allEntries = true)
    public void delete(Set<Integer> ids) {
    }

    @CacheEvict(value = sk_CACHE_NAME, allEntries=true)
    public Test add(Test test) {
        return this.testRepository.saveAndFlush(test);
    }

}
